package Monopol;

import Monopol.Cubes.Cube;
import ClientBL.Event;
import GUI.GameBoard.GameBoard;
import GUI.PopUpDialog;
import GameServer.eEventsID;
import Monopol.Cubes.EnterCubes;
import Monopol.Events.MonopolyGameEvents;
import Monopol.MonopolExceptions.MonopolErrors;
import Player.Player;
import Square.City;
import Square.Negotiable;
import Utils.Utils;
import java.awt.Toolkit;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Timer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Level;
import java.util.logging.Logger;

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
/**
 *
 * @author assafmir
 */
public class MonopolyGame {

    private static GameBoard gameBoard; // The Main Swing Game Board
    private static Boolean bUseAutoDice;
    private final Monopol monopol;
    private Integer playerID;
    private Timer gameEventsPolling;
    private String defPlayerName;
    private String defDiceMode;
    private String srvName;
    private Integer srvPort;
    private Cubes monopolCubes;

    /**
     * The Cubes of the Game - 2 Cubes
     * @author Avia Tam
     * @author Assaf Miron
     *
     */
    static public class Cubes {

	private Cube[] cubes;
	private boolean isDouble;

	public Cube[] getCubes() {
	    return cubes;
	}

	public boolean isDouble() {
	    return isDouble;
	}

	/**
	 * Sets the Cubes with Values
	 * @param arrCubes The Cubes Integer Array
	 */
	public void setCubes(Integer[] arrCubes) {
	    // Assuming that Cube 1 is in place 0
	    cubes[0] = new Cube(arrCubes[0]);
	    // Assuming that Cube 1 is in place 1
	    cubes[1] = new Cube(arrCubes[1]);
	    if (arrCubes[0] == arrCubes[1]) {
		isDouble = true;
	    } else {
		isDouble = false;
	    }
	}

	public Cubes() {
	    super();
	    // Initialize the Cubes Array
	    cubes = new Cube[2];
	    // Initialize Each Cube
	    for (int i = 0; i < 2; i++) {
		cubes[i] = new Cube();
	    }
	}

	/**
	 * Rolls the Dices and updates if the result is a Double or not
	 * @return The Sum of both of the Cubes
	 */
	public Integer rollCubes() {
	    Integer num1, num2;
	    // Throw the Cubes
	    num1 = cubes[0].throwcube();
	    num2 = cubes[1].throwcube();

	    // Check if its a Double
	    if (num1 == num2) {
		isDouble = true;
	    } else {
		isDouble = false;
	    }

	    // Update the Visual Cubes
	    gameBoard.setCubes(cubes);

	    // Return the Cubes Result
	    return num1 + num2;
	}

	@Override
	public String toString() {
	    return cubes[0].getValue().toString() + ", " + cubes[1].getValue().toString();
	}
    }

    public MonopolyGame() {
	try {
	    // Read the Configuration File
	    getConfigData();
	} catch (FileNotFoundException ex) {
	    Logger.getLogger(MonopolyGame.class.getName()).log(Level.SEVERE, null, ex);
	}
	// Create a New Monopoly Object
	monopol = new Monopol();
	// Initialize the Monopoly Game Board
	monopol.initGame(srvName, srvPort);
    }

    /**
     * Reads (and Writes) The Configuration File
     * Using the Properties Class
     */
    private void getConfigData() throws FileNotFoundException {
	// Get a new Properties Object
	Properties myConfig = new Properties();
	InputStream isConfig = null;
	try {
	    // Get the Application Configuration Resource as Stream
	    isConfig = getClass().getClassLoader().getResourceAsStream("resources/appConfig.xml");

	    // Check that the File Exists and There is a Stream to Use
	    if (isConfig != null) {
		// Load the Configurations from the Configuration XML
		myConfig.loadFromXML(isConfig);
		// Set the Properties to the Game
		srvName = myConfig.getProperty("ServerName", "localhost"); // Set the Server Name
		srvPort = Integer.parseInt(myConfig.getProperty("ServerPort", "8080")); // Set the Server Port
		defPlayerName = myConfig.getProperty("DefaultPlayerName", "Player #"); // Set the Default Player Name
		defDiceMode = myConfig.getProperty("DefaultDiceRollMode", "Auto"); // Set the Default Dice Roll Mode
	    } // The Stream was Invalid
	    else {
		String resourcesPath = getClass().getClassLoader().getResource("resources").getPath().replace("file://", "");
		// Create a New File
		File newConfigFile = new File(resourcesPath + "/appConfig.xml");
		// Create the File
		newConfigFile.createNewFile();
		// Get the File Output Stream - For Writing
		OutputStream outConfig = new FileOutputStream(newConfigFile);
		// Set the Default Values to the Parameters
		srvName = "LocalHost";
		srvPort = 8080;
		defPlayerName = "Player #";
		defDiceMode = "Auto";
		// Save the Properties in the Properties Object
		myConfig.setProperty("ServerName", srvName);
		myConfig.setProperty("ServerPort", srvPort.toString());
		myConfig.setProperty("DefaultPlayerName", defPlayerName);
		myConfig.setProperty("DefaultDiceRollMode", defDiceMode);
		// Store the Properties in the AppConfig File
		myConfig.storeToXML(outConfig, "Monopoly Client Configuration");
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}

    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
	// Create an Object of the Game
	MonopolyGame game = new MonopolyGame();
	// Load the Game
	game.loadGame();
	// Play the Game
	game.playGame();
    }

    /**
     * Loads the Monopoly Game
     * Opens a Monopoly Game Loader for choosing the
     * Players and Viewing the Active Games.
     * This Funciotion Loads the GUI Game Board and Initializes it.
     */
    private void loadGame() {
	// Initialize the Game Players
	Boolean loadGame = monopol.loadGame(srvName, srvPort, defPlayerName, defDiceMode);
	// Save the Player ID
	playerID = monopol.getPlayerID();
	// Create the Cubes
	monopolCubes = new Cubes();
	// Show The Board
	try {
	    // Check if there are any Players in the Game
	    if (loadGame) {
		// Load the Game Board With the Players and all the Squares
		gameBoard = new GameBoard(monopol.getGameSquares(), monopol.getPlayers());
		// Show the Board
		gameBoard.showBoard();
		// Update the Cash in the Game Cashier
		gameBoard.updateCashier(monopol.getCashier());
	    } else {
		// No Players - No Game
		System.exit(1);
	    }
	} catch (Exception e) {
	    // Show the User the Error
	    Utils.showError(null, "Error Initializing the Board\n" + e.getMessage());
	    // Print the Error Stack Trace
	    e.printStackTrace();
	    // Exit the Game
	    System.exit(1);
	}

    }

    /**
     * Start a new Monopoly Game Round.
     * The Function Plays a New Monopoly Game and Implements its Functionality
     */
    private void playGame() {
	final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
	final Lock read = readWriteLock.readLock();
	final Lock write = readWriteLock.writeLock();
	List<Event> events = Collections.synchronizedList(MonopolyGameEvents.getInstance().getEvents());
//	RefreshMonopolyEvents myRefreshEvents = new RefreshMonopolyEvents(events, this);
//	gameEventsPolling = ServerService.getInstance().startPolling("Game Events", myRefreshEvents, 5, 4);
//	gameEventsPolling = new Timer("Game Events", true);
//	gameEventsPolling.schedule(myRefreshEvents, 50000);

	synchronized (events) {
	    // Wait for the First Event
	    while (events.isEmpty()) {
		try {
		    // Inform the User
		    Utils.writeText("Waiting For Server Events");
		    // Wait before fetching the Events
		    events.wait(200);
		    // Add the New Events
		    events.addAll(MonopolyGameEvents.getInstance().getEvents());
		} catch (InterruptedException ex) {
		    Logger.getLogger(MonopolyGame.class.getName()).log(Level.SEVERE, null, ex);
		}
	    }
	}
	// Set a Boolean for The Playing Game
	Boolean gamePlaying = true;
	synchronized (events) {
	    while (gamePlaying) {
		// Get the Events List Iterator
		Iterator<Event> evtIter = events.iterator();
		// Loop the Events in the Monopoly Game Events Array
		while (evtIter.hasNext() && gamePlaying) {
		    // Lock the Class - Allow Read Only
		    read.lock();
		    try {
			// Save the Event
			Event evt = evtIter.next();
			// Verify that the Event is not Null
			if (evt != null) {
			    // Get the Event ID and Check wich Event is it
			    eEventsID parseEvent = MonopolyGameEvents.getInstance().parseEvent(evt.getEventType());
			    // Find the Player in the Game
			    Player player = getPlayerByName(evt.getPlayerName().getValue());
			    // Check if the Event is relevant for our Player
			    if (player != null) {
				// Infrom the Player its his Turn
				informPlayer(player.getUniqueID());
				gameBoard.setPlayer(player);
			    }
			    // Switch between the Different Events
			    switch (parseEvent) {
				case GAME_START: {
				    gameStart();
				    // Break and Restart
				    break;
				}
				case AUTO_DICE_ROLL: {
				    // The Dices were rolled - Save the Result
				    Integer[] evtCubes = new Integer[2];
				    evtCubes[0] = evt.getFirstDiceResult();
				    evtCubes[1] = evt.getSecondDiceResult();
				    // Set the Cubes in the Game
				    monopolCubes.setCubes(evtCubes);
				    // Set the Cubes in the Game
				    gameBoard.setCubes(monopolCubes.getCubes());
				    // Break and Restart
				    break;
				}
				case GAME_OVER: {
				    gameEnd();
				    // Set the Game is Over - Exit the While Loop
				    gamePlaying = false;
				    // Break and Restart
				    break;
				}
				case GAME_WINNER: {
				    gameWinner(player);
				    // Break and Restart
				    break;
				}
				case GET_OUT_JAIL: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					// Notify other Players
					notifyPlayers("The Player", player.getName(), "picked up the 'Get Out of Jail' Card.");
				    }
				    // Set the Player Get out of Prison Card
				    player.setRelease_from_prison_card(true);
                                    // Set the Player as 'In Prison'
                                    player.setIs_in_prison(false);
				    // Break and Restart
				    break;
				}
				case GO_TO_JAIL: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					// Notify other Players
					notifyPlayers("The Player", player.getName(), "Is sent to Prison");
				    }
                                    // Set the Player as 'In Prison'
                                    player.setIs_in_prison(true);
				    // Break and Restart
				    break;
				}
				case ASSET_BOUGHT: {
				    buyAsset(evt.getBoardSquareID(), player);
				    // Break and Restart
				    break;
				}
				case HOUSE_BOUGHT: {
				    buyHouse(evt.getBoardSquareID(), player);
				    // Break and Restart
				    break;
				}
				case PLAYER_LOST: {
				    playerLost(player);
				    // Break and Restart
				    break;
				}
				case PLAYER_MOVE: {
				    movingPlayer(player, monopolCubes.getCubes(), evt.getBoardSquareID(), evt.getNextBoardSquareID());
				    // Break and Restart
				    break;
				}
				case PLAYER_ON_START: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					notifyPlayers("You Are on the Start Square!\nYou will Get 400 Shekels!");
				    }
				    // Break and Restart
				    break;
				}
				case PLAYER_PASS_START: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					notifyPlayers("You Passed the Start Square!\nYou will Get 200 Shekels!");
				    }
				    // Break and Restart
				    break;
				}
				case PLAYER_PAY: {
				    playerPayment(player, // Send the Current Player
					    evt.getPaymentAmount(), // Set the Payment Amount
					    evt.isPaymentToOrFromTreasury(), // Set the 'Where' To Pay
					    evt.isPaymemtFromUser(), // Set the Pay from User
					    evt.getPaymentToPlayerName().getValue()); // Set the Player Name to Pay to
				    // Break and Restart
				    break;
				}
				case PLAYER_RESIGN: {
				    // Check that this Eventis for our Player
				    if(player.getUniqueID() == playerID){
					    playerQuits(player);
				    }
				    // Break and Restart
				    break;
				}
				case PLAYER_ROLL_DICE: {
				    Integer[] retCubes = null;
				    if (player.getUniqueID() == playerID) {
					// Get the Manual Dices from the Player
					retCubes = playerRollDice();
					// Inform the Server
					MonopolyGameEvents.getInstance().setCubesEvent(player.getUniqueID(), evt.getEventID(), retCubes);
				    }
				    // Break and Restart
				    break;
				}
				case PLAYER_USED_GETOUTCARD: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					// Notify that the Player used his Card
					notifyPlayers("The Player", player.getName(), "Used the 'Get Out of Jail' Card.");
				    }
				    // Set the Player Get out of Prison Card
				    player.setRelease_from_prison_card(false);
                                    // Set the Player as 'In Prison'
                                    player.setIs_in_prison(false);
				    // Break and Restart
				    break;
				}
				case PROMPT_BUY_ASSET: {
				    // Verify that the Event Reffers to the Playing Player
				    if (player.getUniqueID() == playerID) {
					Boolean retAnswer = false;
					// Find the Asset from the Square ID
					Negotiable asset = monopol.getAsset(evt.getBoardSquareID());
					// Check if the Player have enough Money to buy
					if (asset.getCost() <= player.getFunds()) {
					    // Ask the Palyer to buy the Asset
					    retAnswer = promptBuyAsset(asset, evt.getTimeoutCount());
					    // Send the Server an Event for the Buying the Asset (Accourding to the Player Response)
					    Boolean result = MonopolyGameEvents.getInstance().setBuyAsset(player.getUniqueID(), evt.getEventID(), retAnswer);
					    if (!result) {
						Utils.showError(gameBoard, "The Buy Operation was not Successfull");
					    }
					}
				    }
				    // Break and Restart
				    break;
				}
				case PROMPT_BUY_HOUSE: {
				    // Verify that the Event Reffers to the Playing Player
				    if (player.getUniqueID() == playerID) {
					Boolean addHouse;
					// Find the Asset from the Square ID
					City asset = (City) monopol.getAsset(evt.getBoardSquareID());
					// Check if the Player have enough Money to buy
					if (asset.getHouse_price() <= player.getFunds()) {
					    // Check if the Player wants to Add another House
					    addHouse = promptAddHouse(asset, evt.getTimeoutCount());
					    // Send the Server an Event for Adding a House to the Asset (Accourding to the Player Response)
					    MonopolyGameEvents.getInstance().setBuyHouse(player.getUniqueID(), evt.getEventID(), addHouse);
					}
				    }
				    // Break and Restart
				    break;
				}
				case SURPRISE_CARD: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					// Show the Player a Surprise Card Message Dialog
					showCardDialog("SURPRISE", evt.getEventMessage().getValue());
				    } else {
					notifyPlayers(evt.getEventMessage().getValue());
				    }
				    // Break and Restart
				    break;
				}
				case WARNING_CARD: {
				    // Check that the Message is for our Player
				    if (player.getUniqueID() == playerID) {
					// Show the Player a Wanrning Card Message Dialog
					showCardDialog("WARNING", evt.getEventMessage().getValue());
				    } else {
					notifyPlayers(evt.getEventMessage().getValue());
				    }
				    // Break and Restart
				    break;
				}
				default: {
				    // Do Nothing...
				    // Break and Restart
				    break;
				}
			    }

			    if (player != null) {
				// Update the Player Panel as well
				gameBoard.updatePlayer(player, player.getGame_location());
			    }
			}
		    } catch (Exception e) {
			e.printStackTrace();
			// Check that the Message is not Empty
			if (e.getMessage() != null && !e.getMessage().isEmpty()) {
			    // Show the Message to the User
			    Utils.showError(gameBoard, e.getMessage());
			}
		    } finally {
			// Update all the Components in the Game Board
			gameBoard.updateAll(monopol.getCashier());
			// Unlock the Class - Allow Write
			read.unlock();
			// Lock the Class - Allow Write Only
			write.lock();
			try {
			    // Verify that the Events Array is Not Empty
			    if (!events.isEmpty()) {
				// Remove the Event - We are Done with it
				evtIter.remove();
			    }
			} catch (Exception ex) {
			    ex.printStackTrace();
			} finally {
			    // Unlock the Class - Allow Read
			    write.unlock();
			}
		    }
		}
		// Lock the Class - Allow Write Only
		write.lock();
		try {
		    // Check if we need more Events
		    if (!evtIter.hasNext()) {
			events.addAll(MonopolyGameEvents.getInstance().getEvents());
		    }
		} catch (Exception ex) {
		    ex.printStackTrace();
		} finally {
		    // Unlock the Class - Allow Read
		    write.unlock();
		}
	    }
	}
    }

    /**
     * Inform the Player it his Turn with a Beep
     * @param currPlayerID The Current Player ID
     */
    public void informPlayer(Integer currPlayerID) {
	// Check if the Event is relevant for our Player
	if (currPlayerID == playerID) {
	    // Infrom the Player its his Turn
	    Toolkit.getDefaultToolkit().beep();
	}
    }

    /**
     * Start the Game
     */
    public void gameStart() {
	// Welcome the Player to the Game
	PopUpDialog welcomeDialog = new PopUpDialog(gameBoard, "Welcome to the Game!", 5);
	// Show The Dialog and Start the Countdown
	welcomeDialog.showMessage();
    }

    /**
     * End the Game
     */
    public void gameEnd() {
	// The Game is Over - Stop the Polling
	if (gameEventsPolling != null) {
	    gameEventsPolling.cancel();
	}
    }

    /**
     * Finishes the Game with a Winner
     * @param player The Winning Player
     */
    public void gameWinner(Player player) {
	try {
	    // The Game Ended - Stop the Polling
	    if (gameEventsPolling != null) {
		gameEventsPolling.cancel();
	    }
	    // No more Players in the Game but 1
	    Utils.writeText("We have a winner!!!");
	    // The Last Player is the Winner - It will be the Only player left in the Array List
	    if (monopol.getPlayers().size() == 1) {
		Utils.writeText("Congratulations to", player.getName());
		// Print Out the Winning Player
		Utils.writeText(player.toString());
	    } else {
		// Write an Error Message
		Utils.writeText("Error!\nThere is more than 1 Player!\n", monopol.getPlayers().toString());
		Utils.writeText("Error!");
	    }
	    // The Game is Over
	    Utils.writeText("Game Over!");
	} catch (MonopolErrors ex) {
	    Utils.showError(gameBoard, "Error Finishing the Game!\n" + ex.getMessage());
	}
    }

    /**
     * Finds the Player in the Monopoly Game
     * @param playerName The Player Name to Find
     * @return The {@link Player} Object
     */
    public Player getPlayerByName(String playerName) {
	return monopol.findPlayer(playerName);
    }

    /**
     * Buys an Asset in the Game
     * @param squareID The Square ID to Buy (The Asset)
     * @param player The Player who Buys the Asset
     */
    public void buyAsset(Integer squareID, Player player) {
	// Buy the Asset in the Game
	monopol.buyAsset(monopol.getAsset(squareID), player);
	// Update the Game GUI of the Changes
	gameBoard.updatePlayer(player, squareID);
    }

    /**
     * Prompting the Player to buy an Asset in the Game
     * @param squareID The Square ID to Buy (The Asset)
     * @return The players' Answer
     */
    public Boolean promptBuyAsset(Negotiable asset, Integer timer) {
	// Initialize the Return value
	Boolean buyAsset = false;
	// Set the Popup Dialog Object with the Asset Details
	PopUpDialog currDiag = new PopUpDialog(gameBoard, asset, timer);
	// Ask player by swing if he wants to setBuyAsset this asset
	buyAsset = currDiag.showBuyDialog();
	// Return the Players Answer
	return buyAsset;
    }

    /**
     * Buys a House on an Asset
     * @param squareID The Square ID to Build the House On (The Asset)
     * @param player The Player who buys the House
     */
    public void buyHouse(Integer squareID, Player player) {
	// Buy the House in the Game
	monopol.buyHouse((City) monopol.getAsset(squareID), player);
	// Add a House drawing on the Board
	gameBoard.buyHouse(player.getGame_location());
    }

    /**
     * Prompting the Player to Ad more Houses on an Asset
     * @return The Players Answer
     */
    public Boolean promptAddHouse(City asset, Integer timer) {
	// Initialize the Return value
	Boolean buyHouse = false;
	String cityName = asset.getName();
	// Set the Popup Dialog Message
	PopUpDialog popDialog = new PopUpDialog(gameBoard, "Do you want to Add more Houses to " + cityName + "?", timer);

	// Ask player by swing if he wants to Add more Houses to this City
	buyHouse = popDialog.showPlayerDialog();
	// Return the Players Answer
	return buyHouse;

    }

    /**
     * Shows a Notification Message to the Players in the Game
     * @param message The Message to Show
     */
    public void notifyPlayers(String... message) {
	StringBuilder sb = new StringBuilder();
	for (String msg : message) {
	    sb.append(msg);
	    sb.append(" ");
	}
	PopUpDialog dialog = new PopUpDialog(gameBoard, sb.toString(), 10);
	dialog.showMessage();
    }

    /**
     * Show the Player a Card Popup Dialog
     * @param type The Card Type (SURPRIS / WARNING)
     * @param message The Card Message to Show
     */
    public void showCardDialog(String type, String message) {
	// Set the Popup Dialog Message
	PopUpDialog popDialog = new PopUpDialog(gameBoard, message, 10);
	// Check the Card Type to show the Corrent Dialog
	if (type.equals("SURPRISE")) {
	    popDialog.showSurpriseDialog();
	} else if (type.equals("WARNING")) {
	    popDialog.showWarningDialog();
	}
    }

    /**
     * Invokes when a Player Loses the Game 
     * @param player The Player Name
     */
    public void playerLost(Player player) {
	try {
	    // Notify Other Players
	    Utils.writeText(player.getName(), "Lost the Game.");
	    // Delete the Player who Lost from the game
	    Boolean deletePlayer = monopol.deletePlayer(player);
	    if (deletePlayer) {
		// Delete the Player from the Game
		gameBoard.deletePlayer(player.getNumber(), player.getGame_location());
		// Update All the Components in the Game Board
		gameBoard.updateAll(monopol.getCashier());
	    }
	} catch (MonopolErrors ex) {
	    Utils.showError(gameBoard, "Error on Player Losing the Game!\n" + ex.getMessage());
	}
    }

    /**
     * Moving the Player in the Game
     * @param player The Player To Move
     * @param gameCubes The Total Game Cubes
     * @param currSquareID The Current Square Id the Player is On
     * @param nextSquareID The Next Square ID the Player needs to Be
     */
    public void movingPlayer(Player player, Cube[] gameCubes, Integer currSquareID, Integer nextSquareID) {
	Integer howMany = gameCubes[0].getValue() + gameCubes[1].getValue();
	// Move the Player in the Game
	monopol.movePlayer(player, howMany);
	// Move the Player to the Next Square on the Board
	gameBoard.movePlayer(player.getNumber(), currSquareID, nextSquareID, false);
	// Update the Game GUI of the Changes
	gameBoard.updatePlayer(player, nextSquareID);
    }

    /**
     * Invokes when a Player needs to be Payed or Pay someone
     * @param player The Player who needs Payments
     * @param paymentAmount The Amount of Payments
     * @param payToOrFromTreasury Is the Payment To / From the Treasury?
     * @param payFromUser Is the Payment from another Player?
     * @param payToPlayerName The Player name to pay to
     */
    public void playerPayment(Player player, Integer paymentAmount, Boolean payToOrFromTreasury, Boolean payFromUser, String payToPlayerName) {
	// Check if the Payment needs to be done to the Treasury or the Other Players
	if (payToOrFromTreasury) {
	    // Check if the Player needs to Pay or Get Payed from Treasury
	    if (payFromUser) {
		// The Player needs to pay TO the Treasury
		monopol.setCashier(paymentAmount.doubleValue());
		player.addFunds(paymentAmount.doubleValue() * (-1));
		// Printout and notify the player of payment made
		notifyPlayers(player.getName() + " paid " + paymentAmount.doubleValue() + " Shekels.");
	    } else {
		// The player needs to be payed FROM the Treasury
		monopol.setCashier(paymentAmount.doubleValue() * (-1));
		player.addFunds(paymentAmount.doubleValue());
		// Printout and notify the player of payment made
		notifyPlayers(player.getName() + " got " + paymentAmount.doubleValue() + " Shekels.");
	    }
	} // The Player Needs to Pay or be Payed from Other Players
	else {
	    // Get the Player Object to Pay To
	    Player otherPlayer = monopol.findPlayer(payToPlayerName);
	    // Check that the Player Name to Pay to is Not Empty
	    if (!payToPlayerName.isEmpty() && otherPlayer != null) {
		// Check if the Payment needs to be done FROM the Player or TO the Player
		if (payFromUser) {
		    // The Player Need TO Pay another Player
		    player.addFunds(paymentAmount.doubleValue() * (-1));
		    otherPlayer.addFunds(paymentAmount.doubleValue());
		    // Printout and notify the player of payment made
		    notifyPlayers(player.getName() + " paid " + otherPlayer.getName() + " " + paymentAmount.doubleValue() + " Shekels.");
		} else {
		    // The Player Gets Payed FROM another Player
		    player.addFunds(paymentAmount.doubleValue());
		    otherPlayer.addFunds(paymentAmount.doubleValue() * (-1));
		    // Prinout and notify the player of payment made
		    notifyPlayers(otherPlayer.getName() + " paid " + player.getName() + " " + paymentAmount.doubleValue() + " Shekels.");
		}
	    } else {
		PopUpDialog errDialog = new PopUpDialog(gameBoard, "No Player Name to Pay to.", 10);
		errDialog.showErrorMessage();
	    }
	}
    }

    /**
     * Invokes when the Player Resigns from the Game
     * @param player The Player who Resigned
     */
    public void playerQuits(Player player) {
	try {
	    // Notify Other Players
	    Utils.writeText("The Player", player.getName(), "Has Resigned from the Game.");
	    // Delete the Player who Resigned from the game
	    Boolean deletePlayer = monopol.deletePlayer(player);
	    // Check if the Player is ready for Deletion
	    if (deletePlayer) {
		// Delete the Player from the Game
		gameBoard.deletePlayer(player.getNumber(), player.getGame_location());
		// Update All the Components in the Game Board
		gameBoard.updateAll(monopol.getCashier());
		// Update the Server on the Resigend Player
		MonopolyGameEvents.getInstance().setResignedPlayer(player.getUniqueID());
	    }
	} catch (MonopolErrors ex) {
	    Utils.showError(gameBoard, "Error on Player Losing the Game!\n" + ex.getMessage());
	}
    }

    /**
     * Allows the Player to throw the Manual Dices
     * @return The Dices Results
     */
    public Integer[] playerRollDice() {
	Integer[] retCubes = null;
	// Prompt the Player to Roll the Dices (if Automatic Dice Roll)
	if (monopol.getAutomaticDiceMode()) {
	    // Get the Game Cubes
	    retCubes[0] = monopolCubes.getCubes()[0].getValue();
	    retCubes[1] = monopolCubes.getCubes()[1].getValue();
	} // Prompt the Player to Enter the Dices (if Manual Dice Roll)
	else {
	    // Create a Manul Cubes Window with 30 Seconds Timer
	    EnterCubes entCubes = new EnterCubes(30);
	    retCubes = entCubes.showDialog();
	    Cubes manCubes = new Cubes();
	    // Set the Cubes Manually
	    manCubes.setCubes(retCubes);
	    // Set the Cubes in the Game
	    monopolCubes.setCubes(retCubes);
	    // Set the Game Cubes
	    gameBoard.setCubes(manCubes.getCubes());
	}
	// Return the Cubes
	return retCubes;

    }

    /**
     * Sets the Dice Mode
     * True - Using Automatic Dice Roll Mode
     * False - Using Manual Dice Roll Mode
     * @param autoDice The Dice Mode
     */
    public void setAutomaticDiceMode(Boolean autoDice) {
	this.bUseAutoDice = autoDice;
    }

    /**
     * Return the Dice Mode
     * True - Using Automatic Dice Roll Mode
     * False - Using Manual Dice Roll Mode
     * @return The Dice Mode
     */
    public Boolean getAutomaticDiceMode() {
	return bUseAutoDice;
    }
}
